package com.bootdo.imgtext.check;

import static org.bytedeco.javacpp.opencv_imgcodecs.imread;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashSet;

import javax.imageio.ImageIO;

import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.bytedeco.javacpp.opencv_core.DMatch;
import org.bytedeco.javacpp.opencv_core.DMatchVector;
import org.bytedeco.javacpp.opencv_core.DMatchVectorVector;
import org.bytedeco.javacpp.opencv_core.KeyPointVector;
import org.bytedeco.javacpp.opencv_core.Mat;
import org.bytedeco.javacpp.opencv_features2d.DescriptorMatcher;
import org.bytedeco.javacpp.opencv_xfeatures2d.SURF;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.bootdo.check.service.ColorLogo;
import com.bootdo.common.utils.HttpUtils;
import com.bootdo.common.utils.SpringContextHolder;

public class CheckService {

	protected static final Logger log = LoggerFactory.getLogger(CheckService.class);
	protected static final String USER_AGENT = "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0";
	private static final String TMP_PATH = System.getProperty("java.io.tmpdir");

	protected boolean hasLogo;

	protected int imgNum;

	protected int logoImageNum;

	protected String author;

	protected String exception;

	// 当匹配点超过20，则认为图中找到了logo，子类可以重写这个值
	protected int matchedPoint = 20;

	/**
	 * 判断图文是否含有科协logo
	 * 
	 * @return
	 */
	public boolean hasLogo() {
		return hasLogo;
	}

	/**
	 * 获取正文中图片总数
	 * 
	 * @return
	 */
	public int imageNum() {
		return imgNum;
	}

	/**
	 * 获取含logo的图片总数
	 * 
	 * @return
	 */
	public int logoImageNum() {
		return logoImageNum;
	}

	/**
	 * 获取文章作者名，没有则返回null
	 * 
	 * @return
	 */
	public String author() {
		return author;
	}

	public String getException() {
		return exception;
	}

	/**
	 * 检测图片是否有logo
	 * 
	 * @return
	 */
	protected boolean logoMatch(String url) {
		// 根据url获取图片，将图片暂存至本地
		File imgFile = downLoad(url);
		if (!imgFile.exists()) {
			return false;
		}

		try {
			if (imgFile.getAbsolutePath().endsWith(".png")) {
				convert("jpg", imgFile.getAbsolutePath());
				imgFile = new File(imgFile.getAbsolutePath() + ".jpg");
			}
		} catch (Exception e) {
			if (imgFile.exists()) {
				imgFile.delete();
			}
			log.error(e.getMessage(), e);
			return false;
		}

		try (SURF surf = SURF.create(100d, 4, 3, false, true);
				DescriptorMatcher descriptorMatcher = DescriptorMatcher.create(DescriptorMatcher.FLANNBASED);
				KeyPointVector imgPoints = new KeyPointVector();
				Mat imgDescriptors = new Mat();) {
			Mat img = imread(imgFile.getAbsolutePath(), 1);
			if (img.empty()) {
				return false;
			}

			surf.detect(img, imgPoints);
			surf.compute(img, imgPoints, imgDescriptors);
			if (imgDescriptors.isNull() || imgDescriptors.empty() || imgDescriptors.arraySize() == 0
					|| imgPoints.size() < 3) {
				imgFile.delete();
				return false;
			}

			ColorLogo colorLogo = SpringContextHolder.getBean(ColorLogo.BLACK);
			DMatchVectorVector matches = new DMatchVectorVector();
			descriptorMatcher.knnMatch(colorLogo.getDescriptors(), imgDescriptors, matches, 2);

			DMatchVector goodMatches = selectBestMatches(matches);

			imgFile.delete();
			log.debug("matchedPoint=>{}", goodMatches.size());
			if (goodMatches.size() > matchedPoint) {
				return true;
			}

		}
		return false;

	}

	// 下载图片暂存至本地，返回图片
	private File downLoad(String url) {
		HttpGet httpGet = new HttpGet(url);
		httpGet.setHeader("User-Agent", USER_AGENT);
		if (url.contains("?")) {
			url = url.substring(0, url.indexOf('?'));
		}

		File file = new File(TMP_PATH + System.currentTimeMillis() + url.substring(url.lastIndexOf('/') + 1));

		log.debug("file=>{}", file.getAbsolutePath());
		try (FileOutputStream fos = new FileOutputStream(file);) {
			HttpResponse resp = HttpUtils.getHttpClient().execute(httpGet);
			IOUtils.copy(resp.getEntity().getContent(), fos);
			fos.flush();
		} catch (IOException e) {
			log.error(e.getMessage(), e);
		}
		httpGet.releaseConnection();
		return file;
	}

	// inputFormat表示原格式，outputFormat表示转化后的格式
	private void convert(String outputFormat, String src) throws IOException {
		File input = new File(src);
		BufferedImage bim = get24BitImage(ImageIO.read(input));
		File output = new File(src + "." + outputFormat);
		ImageIO.write(bim, outputFormat, output);
		input.delete();

	}

	private static BufferedImage get24BitImage(BufferedImage image) {
		int w = image.getWidth();
		int h = image.getHeight();
		int[] argb = getRGBs(image.getRGB(0, 0, w, h, null, 0, w));
		BufferedImage newImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
		newImage.setRGB(0, 0, w, h, argb, 0, w);
		return newImage;
	}

	// 丢弃Alpha通道
	private static int[] getRGBs(int[] argbs) {
		int[] rgbs = new int[argbs.length];
		for (int i = 0; i < argbs.length; i++) {
			rgbs[i] = argbs[i] & 0xffffff;
		}
		return rgbs;
	}

	private DMatchVector selectBestMatches(DMatchVectorVector matches) {
		DMatchVector goodMatches = new DMatchVector();
		HashSet<Integer> hashSet = new HashSet<>();
		for (int i = 0; i < matches.size(); i++) {
			DMatch m1 = matches.get(i).get(0);
			DMatch m2 = matches.get(i).get(1);

			if (m1.distance() <= m2.distance() * 0.7 && !hashSet.contains(m1.trainIdx())) {
				hashSet.add(m1.trainIdx());
				goodMatches.push_back(m1);
			}
		}
		return goodMatches;
	}

	protected Document getDoc(String url) {
		Document doc = null;
		int i = 0;
		while (++i < 4 && doc == null) {
			try {
				doc = Jsoup.connect(url).timeout(5000).userAgent(USER_AGENT).get();
			} catch (IOException e) {
				log.error(e.getMessage(), e);
				exception = e.toString();
			}
		}
		if (doc != null) {
			exception = null;
		}
		return doc;
	}

	protected void checkImgs(Elements imgs) {
		if (imgs == null || imgs.isEmpty()) {
			imgNum = 0;
			hasLogo = false;
		} else {
			imgNum = imgs.size();
			int count = 0;
			for (Element element : imgs) {
				if (logoMatch(element.absUrl("src"))) {
					count++;
				}
			}
			if (count >= 1) {
				hasLogo = true;
				logoImageNum = count;
			}
		}
	}
}
